#include "Resources.h"
#include "Define.h"
#include "Convert.h"
#include "DebugLog.h"
#include "physfs.h"
#include "rapidxml.hpp"

#include <fstream>

Resources::Resources(void) : _mode(RESOURCE_LOAD_FROM_ARCHIVE)
{
	_spriteDataFptr[RESOURCE_LOAD_FROM_ARCHIVE] = &Resources::getSpriteDataFromArchive;
	_spriteDataFptr[RESOURCE_LOAD_FROM_FILEPATH] = &Resources::getSpriteDataFromFilepath;
	_animLoadFptr[RESOURCE_LOAD_FROM_ARCHIVE] = &Resources::loadAnimationsFromArchive;
	_animLoadFptr[RESOURCE_LOAD_FROM_FILEPATH] = &Resources::loadAnimationsFromFilepath;
	initializeResourcePaths();
}
Resources::~Resources(void){}

Resources* Resources::_instance = nullptr;

Resources* Resources::getSingletonPtr()
{
	if (_instance == nullptr)
	{
		_instance = new Resources();
	}
	return _instance;
}

void Resources::cleanSingleton()
{
	if (_instance != nullptr)
	{
		delete _instance;
		_instance = nullptr;
	}
}

void Resources::initializeResourcePaths()
{
	_resourcePaths.setFilepath(FILEPATH_DATA_DAT);
	if (!_resourcePaths.parse())
	{
		Debug::log("Resources::initializeResourcePaths ERROR: cannot find or read resource paths.");
		abort();
	}
}

void Resources::setMode(int mode, bool everything)
{
	if (mode == (RESOURCE_LOAD_FROM_ARCHIVE || RESOURCE_LOAD_FROM_FILEPATH))
	{
		_mode = mode;
		if (everything)
		{
			TextureManager::getSingletonPtr()->setMode(mode);
			AudioManager::getSingletonPtr()->setMode(mode);
		}
	}
}

inline m_ss Resources::getSpriteDataFromArchive(const std::string& filepath)
{
	if (!PHYSFS_exists(filepath.c_str()))
	{
		Debug::log("m_ss Resources::getSpriteDataFromArchive ERROR: " + filepath + " does not exist");
		abort();
	}

	PHYSFS_file* file = PHYSFS_openRead(filepath.c_str());
	PHYSFS_sint64 file_size = PHYSFS_fileLength(file);
	char* buffer = new char[static_cast<PHYSFS_sint32>(file_size)];
	PHYSFS_read(file, buffer, 1, static_cast<PHYSFS_uint32>(file_size));
	buffer[file_size] = '\0';

	m_ss spriteData;
	std::string sb = "";
	for (int i = 0; i < file_size + 1; i++)
	{

		if ( (buffer[i + 1] == '\n' && buffer[i] == '\r') || (buffer[i] == '\0') )
		{
			size_t find = sb.find_first_of("=");
			spriteData.insert(std::make_pair(sb.substr(0, find), sb.substr(find + 1, sb.length() - find)));
			sb = "";
		}
		else
		{
			if (buffer[i] != '\n' && buffer[i] != '\r' && buffer[i] != ' ')
			{
				sb += buffer[i];
			}
		}
	}
	PHYSFS_close(file);
	PHYSFS_delete(buffer);
	return spriteData;
}

inline m_ss Resources::getSpriteDataFromFilepath(const std::string& filepath)
{
	std::ifstream in(filepath);
	if (!in)
	{
		Debug::log("m_ss Resources::getSpriteDataFromFilepath ERROR: " + filepath + " does not exist");
		abort();
	}
	m_ss spriteData;
	std::string line;
	while (std::getline(in, line))
	{
		size_t find = line.find_first_of("=");
		spriteData.insert(std::make_pair(line.substr(0, find), line.substr(find + 1, line.length() - find)));
	}
	in.close();
	return spriteData;
}

void Resources::loadSprite(sf::Sprite& sprite, const std::string& id)
{
	const char* TEXTURE = "TEXTURE";
	m_ss data = (this->*_spriteDataFptr[_mode])(_resourcePaths.getValue(TEXTURE, id) + ".dat");
	sf::Texture* texture = TextureManager::getSingletonPtr()->loadTexture(_resourcePaths.getValue(TEXTURE, id) + ".png", id);
	sprite.setTexture(*texture);
	sprite.setTextureRect(stringToSfIntRect(data["size"]));
	sprite.setOrigin(stringToSfVec2f(data["origin"]));
	texture->setRepeated(stringToBool(data["repeated"]));
	texture->setSmooth(stringToBool(data["smooth"]));
}

inline void Resources::loadAnimationsFromArchive(AnimationManager& animationManager, const std::string& filepath)
{
	if (!PHYSFS_exists(filepath.c_str()))
	{
		Debug::log("Resources::loadAnimationsFromArchive ERROR: cannot find filepath at " + filepath);
		abort();
	}

	PHYSFS_file* file = PHYSFS_openRead(filepath.c_str());
	PHYSFS_sint64 file_size = PHYSFS_fileLength(file);
	char* buffer = new char[static_cast<PHYSFS_sint32>(file_size)];
	PHYSFS_read(file, buffer, 1, static_cast<PHYSFS_uint32>(file_size));
	buffer[file_size] = '\0';
	std::string xmlContent = buffer;
	PHYSFS_close(file);
	PHYSFS_delete(buffer);
	std::vector<char> _xmlBuffer = std::vector<char>(xmlContent.begin(), xmlContent.end());
	_xmlBuffer.push_back('\0');
	rapidxml::xml_document<> doc;
	doc.parse<rapidxml::parse_no_data_nodes>(&_xmlBuffer[0]);
	rapidxml::xml_node<>* root = doc.first_node();
	rapidxml::xml_node<>* animation = root->first_node("animation");
	
	while (animation)
	{
		std::string id = animation->first_attribute("id")->value();
		int startFrame = atoi(animation->first_attribute("startframe")->value());
		int startupFrame = atoi(animation->first_attribute("startupframe")->value());
		int endframe = atoi(animation->first_attribute("endframe")->value());
		int row = atoi(animation->first_attribute("row")->value());
		int framerate = atoi(animation->first_attribute("framerate")->value());
		std::string repeatString = animation->first_attribute("repeat")->value();
		bool repeat = (repeatString == "true") ? true : false;
		std::string oscillateString = animation->first_attribute("oscillate")->value();
		bool oscillate = (oscillateString == "true") ? true : false;
		std::string rwrString = animation->first_attribute("rwr")->value();
		bool rwr = (rwrString == "true") ? true : false;
		int tr = atoi(animation->first_attribute("tr")->value());
		animationManager.addAnimation(new Animation(id, startFrame, startupFrame, endframe, row, framerate, repeat, rwr, oscillate, tr));
		animation = animation->next_sibling();
	}
}

inline void Resources::loadAnimationsFromFilepath(AnimationManager& animationManager, const std::string& filepath)
{
	std::string xmlContent = "";
	std::ifstream in(filepath);
	if (!in)
	{
		throw "Cannot find " + filepath;
		abort();
	}
	std::string line;
	while (std::getline(in, line))
		xmlContent += line;
	in.close();
	
	std::vector<char> _xmlBuffer = std::vector<char>(xmlContent.begin(), xmlContent.end());
	_xmlBuffer.push_back('\0');
	rapidxml::xml_document<> doc;
	doc.parse<rapidxml::parse_no_data_nodes>(&_xmlBuffer[0]);
	rapidxml::xml_node<>* root = doc.first_node();
	rapidxml::xml_node<>* animation = root->first_node("animation");
	
	while (animation)
	{
		std::string id = animation->first_attribute("id")->value();
		int startFrame = atoi(animation->first_attribute("startframe")->value());
		int startupFrame = atoi(animation->first_attribute("startupframe")->value());
		int endframe = atoi(animation->first_attribute("endframe")->value());
		int row = atoi(animation->first_attribute("row")->value());
		int framerate = atoi(animation->first_attribute("framerate")->value());
		std::string repeatString = animation->first_attribute("repeat")->value();
		bool repeat = (repeatString == "true") ? true : false;
		std::string oscillateString = animation->first_attribute("oscillate")->value();
		bool oscillate = (oscillateString == "true") ? true : false;
		std::string rwrString = animation->first_attribute("rwr")->value();
		bool rwr = (rwrString == "true") ? true : false;
		int tr = atoi(animation->first_attribute("tr")->value());
		animationManager.addAnimation(new Animation(id, startFrame, startupFrame, endframe, row, framerate, repeat, rwr, oscillate, tr));
		animation = animation->next_sibling();
	}
}

void Resources::loadAnimations(AnimationManager& animationManager, const std::string& id)
{
	const char* ANIMATION = "ANIMATION";
	(this->*_animLoadFptr[_mode])(animationManager, _resourcePaths.getValue(ANIMATION, id) + ".xml");
}

void Resources::loadAudio(sf::Sound& sound, const std::string& id)
{
	const char* AUDIO = "AUDIO";
	sound.setBuffer(*AudioManager::getSingletonPtr()->loadSound(_resourcePaths.getValue(AUDIO, id) + ".wav", id));
}